import { Component, OnInit, Input, ChangeDetectionStrategy, ElementRef, ChangeDetectorRef, ViewChild, AfterViewInit, 
  Renderer2, Inject } from '@angular/core';
import { ModalBoxType } from './modal-box.reducer';
import { Store, select } from '@ngrx/store';
import { AppStoreModule } from './store';
import { getModalBox, getModalBoxType, getModalBoxVisible } from './modal-box.selector';
import { Overlay, OverlayRef, OverlayKeyboardDispatcher, BlockScrollStrategy, OverlayContainer } from '@angular/cdk/overlay';
import { BatchActionsService } from './batch-actions.service';
import { ESCAPE } from '@angular/cdk/keycodes';
import { getDomHideSize, getWindowSize, changePointEvents } from 'src/app/utils/dom.util';
import { DOCUMENT } from '@angular/common';
import { trigger, style, state, animate, transition } from '@angular/animations';

@Component({
  selector: 'app-modal-box',
  templateUrl: './modal-box.component.html',
  styleUrls: ['./modal-box.component.sass'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  animations: [
    trigger('showHide', [
      state('show', style({ transform: 'scale(1)', opacity: 1 })),
      state('hide', style({ transform: 'scale(0)', opacity: 0 })),
      transition('show<=>hide', animate('0.1s'))
    ])
  ]
})
export class ModalBoxComponent implements OnInit, AfterViewInit {

  titles = {
    register: '注册',
    login: '登录',
    tip: '提示',
    default: ''
  };

  showModal = 'hide';
  @Input() visible = false;
  @Input() currentModalBoxType = ModalBoxType.Default;
  @Input() showSpin = false;

  private overlayRef: OverlayRef;
  private overlayContainerEl: HTMLElement;
  private blockScrollStrategy: BlockScrollStrategy;
  private resizeHandler: () => void;

  @ViewChild('modalContainer', { static: false }) private modalRef: ElementRef;

  constructor(
    @Inject(DOCUMENT) private doc: Document,
    private $store: Store<AppStoreModule>,
    private overlay: Overlay,
    private overlayContainer: OverlayContainer,
    private elementRef: ElementRef,
    private overlayKeyboardDispatcher: OverlayKeyboardDispatcher,
    private cdr: ChangeDetectorRef,
    private batchActionService: BatchActionsService,
    private rd: Renderer2
  ) {
    const $appStore = this.$store.pipe(select(getModalBox));
    $appStore.pipe(select(getModalBoxVisible)).subscribe(visible => this.modalBoxVisibleMonitor(visible));
    $appStore.pipe(select(getModalBoxType)).subscribe(modalBoxType => this.modalBoxTypeMonitor(modalBoxType));

    this.blockScrollStrategy = this.overlay.scrollStrategies.block();
  }

  ngOnInit(): void {
    this.createOverlay();
  }

  ngAfterViewInit(): void {
    this.overlayContainerEl = this.overlayContainer.getContainerElement();
    this.listenResizeToCenter();
  }

  hide() {
    this.batchActionService.controlModalBox(false);
  }

  private modalBoxVisibleMonitor(visible: boolean) {
    this.visible = this.visible === visible ? this.visible : visible;
    this.showModal = this.visible ? 'show' : 'hide';
    if (this.visible) {
      this.blockScrollStrategy.enable();
      this.overlayKeyboardDispatcher.add(this.overlayRef);
      this.listenResizeToCenter();
      changePointEvents('auto', this.overlayContainerEl);
    } else {
      this.blockScrollStrategy?.disable();
      this.overlayKeyboardDispatcher.remove(this.overlayRef);
      if (this.resizeHandler) {
        this.resizeHandler();
      }
      changePointEvents('none', this.overlayContainerEl);
    }
    this.cdr.markForCheck();
  }

  private modalBoxTypeMonitor(modalBoxType: ModalBoxType) {
    this.currentModalBoxType = this.currentModalBoxType === modalBoxType ? this.currentModalBoxType : modalBoxType;
    this.cdr.markForCheck();
  }

  private listenResizeToCenter() {
    const modal = this.modalRef.nativeElement;
    this.keepCenter(modal);

    this.resizeHandler = this.rd.listen('window', 'resize', () => this.keepCenter(modal));
  }

  private keepCenter(modal: HTMLElement) {
    const modalSize = getDomHideSize(modal);
    const windowSize = getWindowSize();

    const modalLeft = (windowSize.width - modalSize.width) / 2;
    const modalTop = (windowSize.height - modalSize.height) / 2;

    modal.style.left = modalLeft + 'px';
    modal.style.top = modalTop + 'px';
  }

  private createOverlay() {
    this.overlayRef = this.overlay.create();
    this.overlayRef.overlayElement.appendChild(this.elementRef.nativeElement);
    this.overlayRef.keydownEvents().subscribe(e => this.keyDownListener(e));
  }

  private keyDownListener(e: KeyboardEvent) {
    if (e.keyCode === ESCAPE) {
      this.hide();
    }
  }
}
